package com.ellwood.IPGuard.common.core.utils;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.URLUtil;
import cn.hutool.extra.spring.SpringUtil;
import cn.hutool.poi.excel.ExcelUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.converters.longconverter.LongStringConverter;
import com.alibaba.excel.support.ExcelTypeEnum;
import com.ellwood.IPGuard.common.core.excel.ExcelDataListener;
import com.ellwood.IPGuard.common.core.excel.ExcelFinishCallBack;
import com.fhs.common.utils.ConverterUtils;
import com.fhs.core.trans.anno.Trans;
import com.fhs.core.trans.constant.TransType;
import com.fhs.core.trans.util.ReflectUtils;
import com.fhs.core.trans.vo.TransPojo;
import com.fhs.trans.service.impl.DictionaryTransService;
import jakarta.servlet.http.HttpServletResponse;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.lang.reflect.Field;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @ProjectName: IPGuard-boot
 * @PackageName: com.ellwood.IPGuard.common.core.utils
 * @ClassName: EllExcelUtils
 * @Description: // 自定义Excel工具类
 * @Author: Ellwood
 * @CreateDate: 2024/10/11 - 上午9:57
 * @Version: V1.0.0
 */
@Slf4j
public class EllExcelUtils extends ExcelUtil {
        
        /**
         * 读取excel文件
         * @param file Excel文件
         * @param head 列名
         * @param <T> 数据类型
         * @param callBack 回调 导入时传入定义好的回调接口，excel数据解析完毕之后监听器将数据传入回调函数
         *                  这样调用工具类时可以通过回调函数获取导入的数据，如果数据量过大可根据实际情况进行分配入库
         */
        public static <T> void readAnalysis(MultipartFile file, Class<T> head, ExcelFinishCallBack<T> callBack){
                try{
                        EasyExcel.read(file.getInputStream(),head,new ExcelDataListener<>(callBack)).sheet().doRead();
                }catch (IOException e){
                        log.error("读取excel文件失败",e);
                }
        }
        
        /**
         * 读取excel文件
         * @param <T>      数据类型
         * @param file     excel文件
         * @param head     列名
         * @param callBack 回调 导入时传入定义好的回调接口，excel数据解析完毕之后监听器将数据传入回调函数
         *                 这样调用工具类时可以通过回调函数获取导入的数据，如果数据量过大可根据实际情况进行分配入库
         */
        public static <T> void readAnalysis(File file, Class<T> head, ExcelFinishCallBack<T> callBack){
                try {
                        EasyExcel.read(new FileInputStream(file), head, new ExcelDataListener<>(callBack)).sheet().doRead();
                } catch (IOException e) {
                        log.error("readAnalysis error", e);
                }
        }
        
        /**
         * 读取excel文件 --> 同步
         * @param file excel文件
         * @param clazz 模板类
         * @return java.utils.List
         * @param <T> 数据类型
         */
        public static  <T> List<T> readSync(File file,Class<T> clazz){
                return readSync(file,clazz,1,0, ExcelTypeEnum.XLSX);
        }
        
        /**
         * 读取excel文件 同步
         * @param <T>       数据类型
         * @param file      文件
         * @param clazz     模板类
         * @param rowNum    数据开始行 1
         * @param sheetNo   第几张表
         * @param excelType 数据表格式类型
         * @return java.util.List
         */
        public static  <T> List<T> readSync(File file,Class<T> clazz,Integer rowNum,Integer sheetNo,ExcelTypeEnum excelType ){
                return EasyExcel.read(file).headRowNumber(rowNum).excelType(excelType).head(clazz).sheet(sheetNo).doReadSync();
        }
        
        /**
         * 导出数据到文件
         * @param head 类名
         * @param file 文件
         * @param data 数据
         * @param <T> 数据类型
         */
        public static <T> void excelExport(Class<T> head,File file,List<T> data){
                excelExport(head,file,"sheet1",data);
        }
        
        /**
         * 导出数据到文件
         * @param head 类名
         * @param file 文件
         * @param sheetName sheet名称
         * @param data 数据列表
         * @param <T> 写入格式
         */
        public static <T> void excelExport(Class<T> head,File file,String sheetName,List<T> data){
                try{
                        EasyExcel.write(file,head).sheet(sheetName).registerConverter(new LongStringConverter()).doWrite(data);
                }catch (Exception e){
                        throw new RuntimeException(e);
                }
        }
        
        /**
         * 导出数据到web
         * 文件下载（失败了会返回一个有部分数据的Excel）
         *
         * @param head      类名
         * @param excelName excel名字
         * @param sheetName sheet名称
         * @param data      数据
         */
        public static <T> void excelExport(Class<T> head,String excelName,String sheetName,List<T> data){
                try{
                        HttpServletResponse response=getExportResponse(excelName);
                        EasyExcel.write(response.getOutputStream(), head)
                                .sheet(StringUtils.isBlank(sheetName) ? "sheet1" : sheetName)
                                .registerConverter(new LongStringConverter()).doWrite(data);
                }catch (Exception e){
                        throw new RuntimeException(e);
                }
        }
        
        /**
         * 导出数据到web
         * 文件下载（失败了会返回一个有部分数据的Excel）
         *
         * @param head      类名
         * @param excelName excel名字
         * @param sheetName sheet名称
         * @param data      数据
         */
        public static <T> void excelExport(List<List<String>> head, String excelName, String sheetName, List<T> data) {
                try {
                        HttpServletResponse response = getExportResponse(excelName);
                        
                        EasyExcel.write(response.getOutputStream())
                                .head(head).sheet(StringUtils.isBlank(sheetName) ? "sheet1" : sheetName)
                                .registerConverter(new LongStringConverter()).doWrite(data);
                } catch (IOException e) {
                        throw new RuntimeException(e);
                }
        }
        
        private static HttpServletResponse getExportResponse(String excelName){
                HttpServletResponse response = HttpContextUtils.getHttpServletResponse();
                response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
                response.setHeader("Access-Control-Expose-Headers", "Content-Disposition");
                response.setCharacterEncoding("UTF-8");
                
                excelName += DateUtil.format(new Date(), "yyyyMMddHHmmss");
                String fileName = URLUtil.encode(excelName, StandardCharsets.UTF_8);
                response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
                
                return response;
        }
        
        /**
         * 解析字典数据到字段上
         * 比如 T中有 genderLabel字段 为男 需要给 gender 字段自动设置为0
         *
         * @param dataList 需要被反向解析的数据
         */
        @SneakyThrows
        public static <T extends TransPojo> void parseDict(List<T> dataList) {
                //没有数据就不需要初始化
                if (CollectionUtil.isEmpty(dataList)) {
                        return;
                }
                Class<? extends TransPojo> clazz = dataList.get(0).getClass();
                //拿到所有需要反向翻译的字段
                List<Field> fields = ReflectUtils.getAnnotationField(clazz, Trans.class);
                //过滤出字典翻译
                fields = fields.stream().filter(field -> TransType.DICTIONARY.equals(field.getAnnotation(Trans.class).type())).collect(Collectors.toList());
                DictionaryTransService dictionaryTransService = SpringUtil.getBean(DictionaryTransService.class);
                for (T data : dataList) {
                        for (Field field : fields) {
                                Trans trans = field.getAnnotation(Trans.class);
                                // key不能为空并且ref不为空的才自动处理
                                if (StrUtil.isAllNotBlank(trans.key(), trans.ref())) {
                                        Field ref = ReflectUtils.getDeclaredField(clazz, trans.ref());
                                        ref.setAccessible(true);
                                        // 获取字典反向值
                                        String value = dictionaryTransService.getDictionaryTransMap().get("un_trans:" + trans.key() + "_" + ref.get(data));
                                        if (StringUtils.isBlank(value)) {
                                                continue;
                                        }
                                        // 一般目标字段是int或者string字段 后面有添加单独抽离方法
                                        if (Integer.class.equals(field.getType())) {
                                                field.setAccessible(true);
                                                field.set(data, ConverterUtils.toInteger(value));
                                        } else {
                                                field.setAccessible(true);
                                                field.set(data, ConverterUtils.toString(value));
                                        }
                                }
                        }
                }
                
        }
}
